/*
	Penjin is Copyright (c)2005, 2006, 2007, 2008, 2009, 2010 Kevin Winfield-Pantoja

	This file is part of Penjin.

	Penjin is free software: you can redistribute it and/or modify
	it under the terms of the GNU Lesser General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	Penjin is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU Lesser General Public License for more details.

	You should have received a copy of the GNU Lesser General Public License
	along with Penjin.  If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SIMPLEJOY_H
#define SIMPLEJOY_H

#include "PenjinTypes.h"
#include "KeyMapper.h"
#include <iostream>

#if defined(PLATFORM_PANDORA) && !defined(PENJIN_SDL_INPUT)
// && (defined(PENJIN_ES) || defined(PENJIN_ES2))
#include <fstream>
#endif
using std::cout;
/*
TODO: Add Wii Controls/GBA/NDS etc
*/

class SimpleJoy
{
    public:
        enum sjSTATUS
        {
            sjRELEASED = 0,
            sjPRESSED = 1,
            sjHELD = 2
        };
        //  keymapping and a key status for the map.
        struct tKey
        {
            KeyMapKey key;
            sjSTATUS status;
        };
        //  Contains a player's control mapping and pressed buttons.
        struct Player
        {
            //  Control map
            KeyMapper mapper;
            bool mapLoaded;
            //  stored keyboard presses
            vector <tKey> storeKeys;
            //  standard game buttons
            sjSTATUS Start, Select, Up, Down, Left, Right, A, B, X, Y, L, R;
            //  X box check and other quit message checking
            sjSTATUS Quit;

            //  Josystick config and values
            Vector2di deadZone;
            float scaler;
            Vector2di leftStick;
            Vector2di rightStick;

            //  mouse
            Vector2di mouse;
            Vector2di oldMouse;
            sjSTATUS leftClick, rightClick;
		#ifdef PENJIN_PANDORA_TOUCHSCREEN_FIX
			bool leftClickNeedsRelease;
		#endif // PENJIN_PANDORA_TOUCHSCREEN_FIX
            int wheel;

            //  GP2X Specific
        #if defined(PLATFORM_GP2X) || defined(PLATFORM_PC)
            sjSTATUS Click, VolumeUp, VolumeDown,UpLeft, UpRight, DownLeft, DownRight;
        #endif
        };
        SimpleJoy();
        ~SimpleJoy();

        /// Multiplayer options
        /*starting from 1... 1 == 1 player 2 == 2 players*/
        void setNumPlayers(CRuint p);           //  Set how many player controller to track
        /*Player 1 is starting at index 0.*/
        void setPlayer(CRuint p){player = p;}   // set the ownership of inputs to this player
        uint getPlayer()const{return player;}

        PENJIN_ERRORS loadControlMap(CRstring filename)
        {
            PENJIN_ERRORS t = players[player].mapper.loadControlMap(filename);
            if(t == PENJIN_OK)
                players[player].mapLoaded = true;
            return t;
        }

        PENJIN_ERRORS saveControlMap(CRstring filename)
        {
            return players[player].mapper.saveControlMap(filename);
        }

        void update();					//	Get current status of keyboard

        // Check button states
        bool isHeld(const sjSTATUS& bstat)const{return (bstat == sjHELD);}
        bool isPressed(const sjSTATUS& bstat)const{return (bstat == sjPRESSED);}
        bool isReleased(const sjSTATUS& bstat)const{return (bstat == sjRELEASED);}

        sjSTATUS isStart()const{return players[player].Start;}
        sjSTATUS isSelect()const{return players[player].Select;}
        sjSTATUS isUp()const{return players[player].Up;}
        sjSTATUS isDown()const{return players[player].Down;}
        sjSTATUS isLeft()const{return players[player].Left;}
        sjSTATUS isRight()const{return players[player].Right;}
        sjSTATUS isA()const{return players[player].A;}
        sjSTATUS isB()const{return players[player].B;}
        sjSTATUS isX()const{return players[player].X;}
        sjSTATUS isY()const{return players[player].Y;}
        sjSTATUS isL()const{return players[player].L;}
        sjSTATUS isR()const{return players[player].R;}
        /// The fabled ANY button.
        sjSTATUS isAny()const
        {
            if((players[player].A || players[player].B || players[player].X || players[player].Y || players[player].L
            || players[player].R || players[player].Start || players[player].Select) == sjPRESSED)
                return sjPRESSED;
            else if((players[player].A || players[player].B || players[player].X || players[player].Y || players[player].L
            || players[player].R || players[player].Start || players[player].Select) == sjHELD)
                return sjHELD;
            return sjRELEASED;
        }

        sjSTATUS isQuit()const{return players[player].Quit;}

    #if defined(PLATFORM_GP2X) || defined(PLATFORM_PC)
        /// GP2X Buttons
        sjSTATUS isClick()const{return players[player].Click;}
        sjSTATUS isVolumeUp()const{return players[player].VolumeUp;}
        sjSTATUS isVolumeDown()const{return players[player].VolumeDown;}
        sjSTATUS isUpLeft()const{return players[player].UpLeft;}
        sjSTATUS isUpRight()const{return players[player].UpRight;}
        sjSTATUS isDownLeft()const{return players[player].DownLeft;}
        sjSTATUS isDownRight()const{return players[player].DownRight;}
    #else
        sjSTATUS isUpLeft()const{return sjRELEASED;}
        sjSTATUS isUpRight()const{return sjRELEASED;}
        sjSTATUS isDownLeft()const{return sjRELEASED;}
        sjSTATUS isDownRight()const{return sjRELEASED;}
    #endif

        /// Keyboard passthrough
        sjSTATUS isKey(CRstring k)
        {
            KeyMapKey t(k);
            for(int i = players[player].storeKeys.size()-1; i>=0;--i)
            {
                if(t.getKey() == players[player].storeKeys.at(i).key.getKey())
                    return players[player].storeKeys.at(i).status;
            }
            return sjRELEASED;
        }
        string isKeyLetter();

        /// Joystick
        void setDeadZone(const Vector2di& zone){players[player].deadZone = zone;}
        void setDeadZoneX(CRint x){players[player].deadZone.x = x;}
        void setDeadZoneY(CRint y){players[player].deadZone.y = y;}
        void setScaler(CRfloat s){players[player].scaler = s;}
        Vector2di getLeftStick()const{return players[player].leftStick;}
        Vector2di getRightStick()const{return players[player].rightStick;}
        int getLeftStickX()const{return players[player].leftStick.x;}
        int getLeftStickY()const{return players[player].leftStick.y;}
        int getRightStickX()const{return players[player].rightStick.x;}
        int getRightStickY()const{return players[player].rightStick.y;}

        /// Joystick logical
        sjSTATUS isLeftStick()const
        {
            if((isLeftStickDown() || isLeftStickLeft() || isLeftStickRight() || isLeftStickUp())==sjHELD)
                return sjHELD;
            else if((isLeftStickDown() || isLeftStickLeft() || isLeftStickRight() || isLeftStickUp())==sjPRESSED)
                return sjPRESSED;
            return sjRELEASED;
        }
        sjSTATUS isLeftStickUp()const
        {
            if(getLeftStickY() < -players[player].deadZone.y)
                return sjHELD;
            return sjRELEASED;
        }
        sjSTATUS isLeftStickDown()const
        {
            if(getLeftStickY() > players[player].deadZone.y)
                return sjHELD;
            return sjRELEASED;
        }
        sjSTATUS isLeftStickLeft()const
        {
            if(getLeftStickX() < -players[player].deadZone.x)
                return sjHELD;
            return sjRELEASED;
        }
        sjSTATUS isLeftStickRight()const
        {
            if(getLeftStickX() > players[player].deadZone.x)
                return sjHELD;
            return sjRELEASED;
        }

        sjSTATUS isRightStick()const
        {
            if((isRightStickDown() || isRightStickLeft() || isRightStickRight() || isRightStickUp())==sjHELD)
                return sjHELD;
            else if((isRightStickDown() || isRightStickLeft() || isRightStickRight() || isRightStickUp())==sjPRESSED)
                return sjPRESSED;
            return sjRELEASED;
        }
        sjSTATUS isRightStickUp()const
        {
            if(getRightStickY() < -players[player].deadZone.y)
                return sjHELD;
            return sjRELEASED;
        }
        sjSTATUS isRightStickDown()const
        {
            if(getRightStickY() > players[player].deadZone.y)
                return sjHELD;
            return sjRELEASED;
        }
        sjSTATUS isRightStickLeft()const
        {
            if(getRightStickX() < -players[player].deadZone.x)
                return sjHELD;
            return sjRELEASED;
        }
        sjSTATUS isRightStickRight()const
        {
            if(getRightStickX() > players[player].deadZone.x)
                return sjHELD;
            return sjRELEASED;
        }

        /// Mouse
        Vector2di getMouse()const{return players[player].mouse;}
        int getMouseX()const{return players[player].mouse.x;}
        int getMouseY()const{return players[player].mouse.y;}
        Vector2di getMouseDelta()const{return players[player].mouse-players[player].oldMouse;}
        int getMouseDeltaX()const{return players[player].mouse.x-players[player].oldMouse.x;}
        int getMouseDeltaY()const{return players[player].mouse.y-players[player].oldMouse.y;}
        // returns mouse wheel movement since last reset (wheel-up will add 1, wheel-down substract 1)
        int getMouseWheelDelta()const{return players[player].wheel;};

	#ifdef PENJIN_PANDORA_TOUCHSCREEN_FIX
        sjSTATUS isLeftClick()const
        {
        	if ( players[player].leftClickNeedsRelease )
				return sjRELEASED;
        	return players[player].leftClick;
		}
	#else
        sjSTATUS isLeftClick()const{return players[player].leftClick;}
	#endif // PENJIN_PANDORA_TOUCHSCREEN_FIX
        sjSTATUS isRightClick()const{return players[player].rightClick;}
        /// TouchScreen - Just a wrapper to mouse
        Vector2di getTouch()const{return getMouse();}
        int getTouchX()const{return getMouseX();}
        int getTouchY()const{return getMouseY();}
        sjSTATUS isTouch()const{return isLeftClick();}

		void clearEventQueue();
        void resetKeys();

		void resetMousePosition();
        void resetMouseButtons();
        void resetMouseWheel();
        void resetDpad();
        void resetUp();
        void resetDown();
        void resetLeft();
        void resetRight();

        void resetA();
        void resetB();
        void resetX();
        void resetY();
        void resetL();
        void resetR();
        void resetStart();
        void resetSelect();

        /// Status
        void joystickStatus();
    #if defined(PLATFORM_PANDORA) && !defined(PENJIN_SDL_INPUT)
    // && (defined(PENJIN_ES) || defined(PENJIN_ES2))
        int getTouchPressure(){return ts_pressure;}
    #endif

    private:
        void mappedDown(const SIMPLEJOY_MAP& map);
        void mappedUp(const SIMPLEJOY_MAP& map);
        void mappedMouseAxes(const SIMPLEJOY_MAP& map,CRuchar axis);
        void mappedJoyAxes(const SIMPLEJOY_MAP& map);

        //  This object stores a keymapping and button status for each Player
        Player* players;
        size_t numPlayers;
        uint player;

    #if defined(PLATFORM_PANDORA) && !defined(PENJIN_SDL_INPUT)
    // && (defined(PENJIN_ES) || defined(PENJIN_ES2))
        #define DEV_NUBL 0
        #define DEV_NUBR 1
        #define DEV_KEYS 2
        #define DEV_GPIO 3
        #define DEV_TOUCH 4
        #define PND_NUBL "nub0"
        #define PND_NUBR "nub1"
        #define PND_KEYS "keypad"
        #define PND_GPIO "gpio-keys"
        #define PND_TOUCH "touchscreen"
        #define NUB_CUTOFF 5
        #define NUB_SCALE  10

        int PND_OpenEventDeviceByName( const char device_name[] );
        void PND_ReadEvents( int fd, int device );
        void PND_CheckEvent( struct input_event *event, int device );

        int fd_nubL, fd_nubR, fd_keys, fd_gpio, fd_touch;
        int rd, version, i;
        int ts_pressure;
        int pointercal[7];  // stores ts cali data
        Point2di rawTS;
        bool haveTSx;
        bool haveTSy;
        struct input_event ev[64];
        uint currentEvent;          //  stores the currently checked event for mapping purposes.
        char event_name[30];
        char dev_name[256]; //= "Unknown";
        unsigned short id[4];
        //Vector2di nubL, nubR;
        // KEV nub code
        //void MappedNubAxes(const SIMPLEJOY_MAP& map, CRint axis);
    #else
        SDL_Joystick **Joy;		//	SDL joysticks
        SDL_Event Event;
    #endif
};

#endif // SIMPLEJOY_H
